﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using FlatRedBall;
using FlatRedBall.Graphics;
using FlatRedBall.Math.Geometry;
using FlatRedBall.Input;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;

using FlatRedBall.Graphics.Animation;

using HarpoonMan.Utilities;

namespace HarpoonMan.PlayerObjects
{
    public enum JumpState
    {
        Jumping,
        Idle
    }

    public enum Direction
    {
        Left,
        Right
    }

    public class Player : PositionedObject
    {
        #region Fields

        private AxisAlignedRectangle _collision;
        private string _contentManagerName;
        public Sprite _sprite;
        private const float PLAYERSPEED = 7.0f;
        private const float JUMPVELOCITY = 15.0f;
        private Harpoon _harpoon;
        private JumpState _jump;

        private Sprite _chainSprite;
        
        private Direction _dir;

        #endregion

        #region Properties

        public AxisAlignedRectangle Collision
        {
            get { return _collision; }
        }

        public Harpoon Harpoon
        {
            get { return _harpoon; }
        }

        public JumpState Jump
        {
            get { return _jump; }
            set { _jump = value; }
        }


        
        #endregion

        #region Methods

        // Constructor
        public Player(string contentManagerName)
        {
            // Set the ContentManagerName and call Initialize:
            _contentManagerName = contentManagerName;
            this.Acceleration.Y = GameManager.GRAVITY;

            // If you don't want to add to managers, make an overriding constructor
            Initialize(true);
        }


        protected virtual void Initialize(bool addToManagers)
        {
            // Here you can preload any content you will be using
            // like .scnx files or texture files.
            if (addToManagers)
            {
                AddToManagers(null);
            }
        }

        public virtual void AddToManagers(Layer layerToAddTo)
        {
            SpriteManager.AddPositionedObject(this);

            AnimationChainList ani = new AnimationChainList();
            ani.Add(LoadIdleRight());
            ani.Add(LoadIdleLeft());
            ani.Add(LoadRunRight());
            ani.Add(LoadRunLeft());
            ani.Add(LoadThrowRight());
            ani.Add(LoadThrowLeft());
            ani.Add(LoadRetractRight());
            ani.Add(LoadRetractLeft());
            ani.Add(LoadJumpRight());
            ani.Add(LoadJumpLeft());

            _sprite = SpriteManager.AddSprite(ani);
            _sprite.PixelScale();
            _sprite.ScaleX *= .5f;
            _sprite.ScaleY *= .5f;

            _sprite.AttachTo(this, false);

            _chainSprite = SpriteManager.AddSprite("redball.bmp");
            _chainSprite.ScaleY = 0.1f;
            _chainSprite.AttachTo(this, false);

            _collision = ShapeManager.AddAxisAlignedRectangle();
            _collision.AttachTo(this, false);
            _collision.ScaleX = _sprite.ScaleX * .4f;
            _collision.ScaleY = _sprite.ScaleY;

            _harpoon = new Harpoon("Global");
            _chainSprite.Visible = _harpoon.Visible;

            _dir = Direction.Right;
            _jump = JumpState.Idle;

            _collision.Visible = false;
        }

        public virtual void Activity()
        {
            HandleMovement();
            HandleHarpoon();
            UpdateChain();

            if (_sprite.CurrentChainName != "RunLeft" || _sprite.CurrentChainName != "RunRight")
            {
                if (_sprite.JustCycled && _jump == JumpState.Jumping)
                {
                    _sprite.Animate = false;
                    _sprite.CurrentFrameIndex = _sprite.CurrentChain.Count - 1;
                }
                else if (_sprite.JustCycled)
                {
                    _sprite.CurrentChainName = (_dir == Direction.Right) ? "IdleRight" : "IdleLeft";
                }
            }
        }

        public virtual void Destroy()
        {
            SpriteManager.RemovePositionedObject(this);

            SpriteManager.RemoveSprite(_sprite);
            ShapeManager.Remove(_collision);
        }

        #endregion

        #region Private Methods

        private void HandleHarpoon()
        {
            if (_harpoon.State == HarpoonState.Idle && 
                InputManager.Mouse.ButtonReleased(Mouse.MouseButtons.LeftButton))
            {
                _harpoon.Velocity = Vector3.Zero;
                _harpoon.State = HarpoonState.Throwing;
                _harpoon.Position = this.Position;
                _harpoon.Visible = true;
                _harpoon.ThrowVector = new Vector3(GameManager.ReticlePos.X - this.X,
                                               GameManager.ReticlePos.Y - this.Y, 0.0f);
                _sprite.CurrentChainName = (_dir == Direction.Right) ? "ThrowRight" : "ThrowLeft";
                _sprite.CurrentFrameIndex = 0;
                _sprite.Animate = true;
            }
            else if (((_harpoon.State == HarpoonState.Grabbed || _harpoon.State == HarpoonState.Crashed) &&
                InputManager.Mouse.ButtonReleased(Mouse.MouseButtons.LeftButton)) || (_harpoon.Position - this.Position).Length() > 20.0f)
            {
                _harpoon.Acceleration = Vector3.Zero;
                _harpoon.State = HarpoonState.Retracting;
                _sprite.CurrentChainName = (_dir == Direction.Right) ? "RetractRight" : "RetractLeft";
                _sprite.CurrentFrameIndex = 0;
                _sprite.Animate = true;
            }

            if (_harpoon.State == HarpoonState.Retracting)
            {
                if (InputManager.Mouse.ButtonPushed(Mouse.MouseButtons.LeftButton))
                {
                    _harpoon.DropBox();
                }

                if (!_harpoon.Collision.CollideAgainst(this.Collision))
                {
                    _harpoon.Retract(this.Position - _harpoon.Position);
                }
                else
                {
                    _harpoon.Visible = false;
                    _harpoon.Velocity = Vector3.Zero;
                    _harpoon.Acceleration = Vector3.Zero;
                    _harpoon.State = HarpoonState.Idle;
                    _harpoon.DropBox();
                }
            }
            else
            {
                _harpoon.Activity();
            }
        }

        private void HandleMovement()
        {
            if (InputManager.Keyboard.KeyDown(Microsoft.Xna.Framework.Input.Keys.A)
                && (InputManager.Keyboard.KeyDown(Microsoft.Xna.Framework.Input.Keys.D) 
                    || InputManager.Keyboard.KeyDown(Microsoft.Xna.Framework.Input.Keys.E)))
            {
                this.Velocity.X = 0f;
                if (_jump != JumpState.Jumping)
                {
                    _sprite.CurrentChainName = (_dir == Direction.Right) ? "IdleRight" : "IdleLeft";
                }
            }
            else if (InputManager.Keyboard.KeyDown(Microsoft.Xna.Framework.Input.Keys.A))
            {
                this.Velocity.X = -PLAYERSPEED;
                _dir = Direction.Left;
                if (_jump != JumpState.Jumping)
                {
                    _sprite.CurrentChainName = "RunLeft";
                }
            }
            else if (InputManager.Keyboard.KeyDown(Microsoft.Xna.Framework.Input.Keys.D)
                || InputManager.Keyboard.KeyDown(Microsoft.Xna.Framework.Input.Keys.E))
            {
                this.Velocity.X = PLAYERSPEED;
                _dir = Direction.Right;
                if (_jump != JumpState.Jumping)
                {
                    _sprite.CurrentChainName = "RunRight";
                }
            }

            if (InputManager.Keyboard.KeyReleased(Microsoft.Xna.Framework.Input.Keys.A)
                || InputManager.Keyboard.KeyReleased(Microsoft.Xna.Framework.Input.Keys.D)
                || InputManager.Keyboard.KeyReleased(Microsoft.Xna.Framework.Input.Keys.E))
            {
                this.Velocity.X = 0f;
                if (_jump != JumpState.Jumping)
                {
                    _sprite.CurrentChainName = (_dir == Direction.Right) ? "IdleRight" : "IdleLeft";
                }
            }

            if ((InputManager.Keyboard.KeyDown(Microsoft.Xna.Framework.Input.Keys.W)
                || InputManager.Keyboard.KeyDown(Microsoft.Xna.Framework.Input.Keys.OemComma)
                || InputManager.Keyboard.KeyDown(Microsoft.Xna.Framework.Input.Keys.Space)) 
                && _jump == JumpState.Idle)
            {
                _jump = JumpState.Jumping;
                this.Velocity.Y = JUMPVELOCITY;
                _sprite.CurrentChainName = (_dir == Direction.Right) ? "JumpRight" : "JumpLeft";
                _sprite.CurrentFrameIndex = 0;
            }
            
        }

        private void UpdateChain()
        {
            Vector3 dist = _harpoon.Position - this.Position;

            float rotateTowards = (float)Math.Atan2(dist.Y, dist.X);

            _chainSprite.RelativeRotationZ = rotateTowards;

            _chainSprite.Visible = _harpoon.Visible;
            _chainSprite.RelativeX = _chainSprite.ScaleX = dist.X / 2.0f;
            _chainSprite.RelativeY = dist.Y / 2.0f;
        }

        #endregion

        #region Animation Loading Methods

        private AnimationChain LoadIdleRight()
        {
            AnimationChain ani = new AnimationChain();
            ani.Add(new AnimationFrame(@"Content/Player/Idle/Idle0000", 1.0f / 30.0f, "Global"));
            ani.Name = "IdleRight";
            return ani;
        }

        private AnimationChain LoadIdleLeft()
        {
            AnimationChain ani = new AnimationChain();
            ani.Add(new AnimationFrame(@"Content/Player/Idle/Idleback0000", 1.0f / 30.0f, "Global"));
            ani.Name = "IdleLeft";
            return ani;
        }

        private AnimationChain LoadRunRight()
        {
            AnimationChain ani = new AnimationChain();
            for (int i = 0; i < 31; i++)
            {
                string number = (i < 10) ? "0" + i.ToString() : i.ToString();
                ani.Add(new AnimationFrame(@"Content/Player/Run/Run00" + number, 1.0f / 30.0f, "Global"));
            }
            ani.Name = "RunRight";
            return ani;
        }

        private AnimationChain LoadRunLeft()
        {
            AnimationChain ani = new AnimationChain();
            for (int i = 0; i < 31; i++)
            {
                string number = (i < 10) ? "0" + i.ToString() : i.ToString();
                ani.Add(new AnimationFrame(@"Content/Player/Run/Runback00" + number, 1.0f / 30.0f, "Global"));
            }
            ani.Name = "RunLeft";
            return ani;
        }

        private AnimationChain LoadThrowRight()
        {
            AnimationChain ani = new AnimationChain();
            for (int i = 0; i < 12; i++)
            {
                string number = (i < 10) ? "0" + i.ToString() : i.ToString();
                ani.Add(new AnimationFrame(@"Content/Player/Throw/Throw00" + number, 1.0f / 30.0f, "Global"));
            }
            ani.Name = "ThrowRight";
            return ani;
        }

        private AnimationChain LoadThrowLeft()
        {
            AnimationChain ani = new AnimationChain();
            for (int i = 0; i < 12; i++)
            {
                string number = (i < 10) ? "0" + i.ToString() : i.ToString();
                ani.Add(new AnimationFrame(@"Content/Player/Throw/Throwback00" + number, 1.0f / 30.0f, "Global"));
            }
            ani.Name = "ThrowLeft";
            return ani;
        }

        private AnimationChain LoadRetractRight()
        {
            AnimationChain ani = new AnimationChain();
            for (int i = 0; i < 15; i++)
            {
                string number = (i < 10) ? "0" + i.ToString() : i.ToString();
                ani.Add(new AnimationFrame(@"Content/Player/Retract/Retract00" + number, 1.0f / 30.0f, "Global"));
            }
            ani.Name = "RetractRight";
            return ani;
        }

        private AnimationChain LoadRetractLeft()
        {
            AnimationChain ani = new AnimationChain();
            for (int i = 0; i < 15; i++)
            {
                string number = (i < 10) ? "0" + i.ToString() : i.ToString();
                ani.Add(new AnimationFrame(@"Content/Player/Retract/Retractback00" + number, 1.0f / 30.0f, "Global"));
            }
            ani.Name = "RetractLeft";
            return ani;
        }

        private AnimationChain LoadJumpRight()
        {
            AnimationChain ani = new AnimationChain();
            for (int i = 0; i < 38; i++)
            {
                string number = (i < 10) ? "0" + i.ToString() : i.ToString();
                ani.Add(new AnimationFrame(@"Content/Player/Jump/Jumpforward00" + number, 1.0f / 30.0f, "Global"));
            }
            ani.Name = "JumpRight";
            return ani;
        }

        private AnimationChain LoadJumpLeft()
        {
            AnimationChain ani = new AnimationChain();
            for (int i = 0; i < 38; i++)
            {
                string number = (i < 10) ? "0" + i.ToString() : i.ToString();
                ani.Add(new AnimationFrame(@"Content/Player/Jump/Jumpback00" + number, 1.0f / 30.0f, "Global"));
            }
            ani.Name = "JumpLeft";
            return ani;
        }

        #endregion
    }
}
